from typing import List, Dict

from overrides import overrides
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, PDFTextExtractionNotAllowed
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTTextBoxHorizontal, LAParams
from pdfminer.pdfparser import PDFParser, PDFDocument
from io import StringIO
import os
import json
from abc import abstractmethod
import logging


class Doc(object):
    def __init__(self, file_path: str, content: str = None):
        self.file_path = file_path
        file_state = os.stat(file_path)
        self._size = self._format_size(file_state.st_size)
        self.file_name = os.path.basename(file_path)
        self._content = content
        self._content_length = len(content) if content is not None else 0
        self.triple_dict: Dict = {}  # {"predict_triple": ..., "text":...}

    @staticmethod
    def _format_size(size):
        for (scale, label) in [(1024 * 1024 * 1024, "GB"), (1024 * 1024, "MB"), (1024, "KB")]:
            if size >= scale:
                return "%.2f %s" % (size * 1.0 / scale, label)
            elif size == 1:
                return "1byte"
            else:  # 小于一字节
                byte = "%.2f" % (size or 0)
        return (byte[:-3]) if byte.endswith(".00") else byte

    def set_content(self, content: str):
        self._content = content
        self._content_length = len(content)

    def get_content(self):
        return self._content, self._content_length

    def get_file_size(self):
        return self._size

    def set_triple_dict(self, triple_dict: Dict):
        self.triple_dict = triple_dict

    def get_triple_dict(self):
        return self.triple_dict

    def get_path(self):
        return self.file_path

    def toString(self) -> str:
        self.__dict__.pop("triple_list")
        return json.dumps(self.__dict__)


class AbstractLoader(object):
    def __init__(self, file_path):
        if os.path.exists(file_path):
            self._doc_path = file_path
        else:
            self._doc_path = None
            raise "在PDF_loader中传入的文件路径错误{}".format(file_path)
        self._doc = None
        self._FILE = open(self._doc_path, "rb")

    def _set_file_message(self, content: str):
        self._doc = Doc(file_path=self._doc_path)
        self._doc.set_content(content)

    @abstractmethod
    def _get_text(self) -> str:
        pass

    def load(self, file_path: str = None) -> Doc:
        """pdf_file:param PDF 文件的路径"""
        if file_path is not None:
            self.__init__(file_path=file_path)

        str_temp = self._get_text()
        self._set_file_message(str_temp)
        self._FILE.close()
        return self._doc


class PDFLoader(AbstractLoader):
    def __init__(self, pdf_file: str):
        super(PDFLoader, self).__init__(pdf_file)
        self.string_io = StringIO(initial_value="")
        logging.getLogger('pdfminer').setLevel(logging.ERROR)

    def _parse_pdf(self):
        """解析PDF文本，并保存到ret_str io缓存中"""
        # 用文件对象创建一个PDF文档分析器
        parser = PDFParser(self._FILE)
        # 创建一个PDF文档
        doc = PDFDocument()
        # 连接分析器，与文档对象
        parser.set_document(doc)
        doc.set_parser(parser)

        # 提供初始化密码，如果没有密码，就创建一个空的字符串
        doc.initialize()

        # 检测文档是否提供txt转换，不提供就忽略
        if not doc.is_extractable:
            raise PDFTextExtractionNotAllowed
        else:
            # 创建PDF，资源管理器，来共享资源
            resource_manager = PDFResourceManager()
            # 创建一个PDF设备对象
            la_params = LAParams()
            device = PDFPageAggregator(resource_manager, laparams=la_params)
            # 创建一个PDF解释其对象
            interpreter = PDFPageInterpreter(resource_manager, device)

            # 循环遍历列表，每次处理一个page内容
            # doc.get_pages() 获取page列表
            for page in doc.get_pages():
                interpreter.process_page(page)
                # 接受该页面的LTPage对象
                layout = device.get_result()
                # 这里layout是一个LTPage对象 里面存放着 这个page解析出的各种对象
                # 一般包括LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等等
                # 想要获取文本就获得对象的text属性，
                for x in layout:
                    if isinstance(x, LTTextBoxHorizontal):
                        # print("当前文本快的宽度为：{}高度为：{}\n\n".format(x.width, x.height))
                        temp = x.get_text()
                        if x.width * x.height < 6000:
                            continue
                        self.string_io.write(temp)
                        # self.string_io.write("\n")

    @overrides
    def _get_text(self) -> str:
        self._parse_pdf()
        return self.string_io.getvalue()


class TextLoader(AbstractLoader):
    def __init__(self, text_file: str):
        super(TextLoader, self).__init__(text_file)

    @overrides
    def _get_text(self) -> str:
        string_io = StringIO(initial_value="")
        for line in self._FILE.readlines():
            s = line.decode("utf-8")
            string_io.write(s)
        return str(string_io.getvalue())
